﻿using System;
using System.Collections.Generic;
using System.Text;
using Beetle;
namespace Beetle.Packages
{
    public class ProtobufPackage : Beetle.HeadSizeOfPackage
    {

        public ProtobufPackage()
        {
        }

        public ProtobufPackage(Beetle.TcpChannel channel)
            : base(channel)
        {

        }

        protected override void WriteMessageType(IMessage msg, BufferWriter writer)
        {

        }

        public override object WriteCast(object message)
        {
            MessageAdapter ma = new MessageAdapter();
            ma.Message = message;
            return ma;
        }

        public override object ReadCast(object message)
        {
            return ((MessageAdapter)message).Message;
        }

        protected override IMessage ReadMessageByType(BufferReader reader, out object typeTag)
        {
            typeTag = "ProtobufAdapter";
            return new MessageAdapter();
        }

        static Dictionary<string, Type> mTypes = new Dictionary<string, Type>(256);

        static Dictionary<Type, string> mNames = new Dictionary<Type, string>(256);

        public static void LoadAssembly()
        {
            try
            {
                string path = AppDomain.CurrentDomain.DynamicDirectory;
                LoadAssembly(path);
                path = AppDomain.CurrentDomain.BaseDirectory;
                LoadAssembly(path);
            }
            catch
            {
            }
        }

        public static void LoadAssembly(string path)
        {
            if (!string.IsNullOrEmpty(path))
            {
                foreach (string item in System.IO.Directory.GetFiles(path, "*.dll"))
                {
                    try
                    {
                        LoadAssembly(System.Reflection.Assembly.LoadFile(item));
                    }
                    catch
                    {
                    }
                }
            }
        }

        public static void LoadAssembly(System.Reflection.Assembly assembly)
        {
            foreach (Type t in assembly.GetTypes())
            {
                ProtoBuf.ProtoContractAttribute[] pc = Smark.Core.Functions.GetTypeAttributes<ProtoBuf.ProtoContractAttribute>(t, false);
                if (pc.Length > 0)
                {
                    string name = t.Name;
                    if (!string.IsNullOrEmpty(pc[0].Name))
                        name = pc[0].Name;
                    mTypes.Add(name, t);
                    mNames.Add(t, name);
                }
            }
        }

        class MessageAdapter : Beetle.IMessage
        {
            public object Message
            {
                get;
                set;
            }

            public void Load(Beetle.BufferReader reader)
            {
                string type = reader.ReadString();
                Beetle.ByteArraySegment segment = ByteArraySegment;
                reader.ReadByteArray(segment);
                using (System.IO.Stream stream = new System.IO.MemoryStream(segment.Array, 0, segment.Count))
                {
                    Message = ProtoBuf.Meta.RuntimeTypeModel.Default.Deserialize(stream, null, mTypes[type]);
                }

            }

            public void Save(Beetle.BufferWriter writer)
            {
                writer.Write(mNames[Message.GetType()]);
                Beetle.ByteArraySegment segment = ByteArraySegment;
                using (System.IO.Stream stream = new System.IO.MemoryStream(segment.Array))
                {
                    ProtoBuf.Meta.RuntimeTypeModel.Default.Serialize(stream, Message);
                    segment.SetInfo(0, (int)stream.Position);

                }
                writer.Write(segment);


            }

            [ThreadStatic]
            private static ByteArraySegment mByteArraySegment = null;

            public ByteArraySegment ByteArraySegment
            {
                get
                {
                    if (mByteArraySegment == null)
                        mByteArraySegment = new ByteArraySegment(TcpUtils.DataPacketMaxLength);
                    return mByteArraySegment;
                }
            }

        }
    }
}
